#include "butils.h"
#include <vector>
#include <cstring>  // strlen
#include <ctime>	// strftime
#ifdef _WIN32
#include <io.h>
#include <direct.h>
#include <objbase.h> // guid
#else
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/resource.h>
#include <uuid/uuid.h> // guid
#endif // !_WIN32

#ifdef _WIN32
std::string butils::current_module_dir()
{
    char path[_MAX_PATH] = { 0 };
    char drive[_MAX_DRIVE] = { 0 };
    char dir[_MAX_DIR] = { 0 };
    char fname[_MAX_FNAME] = { 0 };
    char ext[_MAX_EXT] = { 0 };
    GetModuleFileNameA(nullptr, path, _MAX_PATH);
    _splitpath_s(path, drive, sizeof(drive), dir, sizeof(dir), fname, sizeof(fname), ext, sizeof(ext));
    std::string _dir(drive);
    _dir.append(dir);
    return _dir;
}
#else
int butils::open_dev_null(int fd)
{
	int tmpfd;
	close(fd);
	tmpfd = open("/dev/null", O_RDWR);
	if (tmpfd != -1 && tmpfd != fd) {
		dup2(tmpfd, fd);
		close(tmpfd);
	}
	return (tmpfd != -1) ? 0 : -1;
}

void butils::daemonize(char* dir)
{
	if (0 != fork()) exit(0);

	if (-1 == setsid()) exit(0);

	if (0 != fork()) exit(0);

	if (0 != chdir(dir)) exit(0);

	open_dev_null(STDIN_FILENO);
	open_dev_null(STDOUT_FILENO);
	open_dev_null(STDERR_FILENO);
}

void butils::set_corefile_unlimit()
{
	struct rlimit rlim;
	struct rlimit rlim_new;
	if (getrlimit(RLIMIT_CORE, &rlim) == 0) {
		rlim_new.rlim_cur = rlim_new.rlim_max = RLIM_INFINITY;
		if (setrlimit(RLIMIT_CORE, &rlim_new) != 0) {
			rlim_new.rlim_cur = rlim_new.rlim_max = rlim.rlim_max;
			setrlimit(RLIMIT_CORE, &rlim_new);
		}

	}
}

void butils::set_sockfile_size(size_t size)
{
	struct rlimit rlim;
	struct rlimit rlim_new;
	if (getrlimit(RLIMIT_NOFILE, &rlim) == 0) {
		if (size > 0) {
			rlim_new.rlim_cur = rlim_new.rlim_max = size;
		}
		else {
			rlim_new.rlim_cur = rlim_new.rlim_max = RLIM_INFINITY;
		}
		if (setrlimit(RLIMIT_NOFILE, &rlim_new) != 0) {
			rlim_new.rlim_cur = rlim_new.rlim_max = rlim.rlim_max;
			setrlimit(RLIMIT_NOFILE, &rlim_new);
		}
	}
}

void butils::set_processstack_size(size_t size)
{
	struct rlimit rlim;
	struct rlimit rlim_new;
	if (getrlimit(RLIMIT_STACK, &rlim) == 0) {
		if (size > 0) {
			rlim_new.rlim_cur = rlim_new.rlim_max = size;
		}
		else {
			rlim_new.rlim_cur = rlim_new.rlim_max = RLIM_INFINITY;
		}
		if (setrlimit(RLIMIT_STACK, &rlim_new) != 0) {
			rlim_new.rlim_cur = rlim_new.rlim_max = rlim.rlim_max;
			setrlimit(RLIMIT_STACK, &rlim_new);
		}
	}
}

int butils::write_process_pid(char *dir)
{
	char pidfile[512] = { 0 };
	int n;
	pid_t pid = getpid();
	char pidbuff[64] = { 0 };

	n = snprintf(pidfile, 512, "%s/server.pid", dir);
	pidfile[n] = 0;

	n = snprintf(pidbuff, 64, "%d", pid);
	pidbuff[n] = 0;

	FILE *fp = fopen(pidfile, "w");
	if (!fp) return -1;

	fwrite(pidbuff, n, 1, fp);
	fclose(fp);
	return 0;
}

#endif // !_WIN32

unsigned short butils::swap16(unsigned short x)
{
	return (x >> 8) | (x << 8);
}

unsigned int butils::swap32(unsigned int x)
{
	x = ((x << 8) & 0xFF00FF00) | ((x >> 8) & 0x00FF00FF);
	return (x >> 16) | (x << 16);
}

int butils::create_folder(const std::string& path)
{
	if (path.size() > 256)
		return -1;
	char tmp[256]{ 0 };
	for (size_t i = 0; i < path.size(); ++i)
	{
		tmp[i] = path[i];
		if (tmp[i] == '\\' || tmp[i] == '/' || i + 1 == path.size())
		{
			int ret = 0;
#ifdef _WIN32
			if (_access(tmp, 0) != 0)
			{
				ret = _mkdir(tmp);
			}
#else
			if (access(tmp, 0) != 0)
			{
				ret = mkdir(tmp, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
			}
#endif
			if (ret != 0) return ret;
		}
	}
	return 0;
}

std::string butils::worker_dir()
{
#ifdef _WIN32
    return _getcwd(NULL, 0);
#else
    return getcwd(NULL, 0);
#endif
}

void butils::encrypt_decrypt_xor(std::string& data, const char* key)
{
	static int klen = static_cast<int>(strlen(key));
	char tmpc = 0;
	for (unsigned i = 0; i < data.size(); ++i)
	{
		tmpc = data[i] ^ key[i % klen];
		if (tmpc != 0)
		{ // 要考虑到XOR等于0的情况，如果等于0，就相当于字符串就提前结束了, 这是不可以的；因此tmpc等于0的时候，保持原文不变
			data[i] = tmpc;
		}
	}
}

std::string butils::uuid()
{
    char buf[64] = { 0 };
#ifdef _MSC_VER
    GUID guid;
    if (CoCreateGuid(&guid)) {
        return std::move(std::string(""));
    }
    sprintf_s(buf,
        "%08X-%04X-%04x-%02X%02X-%02X%02X%02X%02X%02X%02X",
        guid.Data1, guid.Data2, guid.Data3,
        guid.Data4[0], guid.Data4[1], guid.Data4[2],
        guid.Data4[3], guid.Data4[4], guid.Data4[5],
        guid.Data4[6], guid.Data4[7]);
#else
    uuid_t uu;
    uuid_generate(uu);
    int index = 0;
    for (int i = 0; i < 16; i++)
    {
        int len = i < 15 ?
            sprintf(buf + index, "%02X-", uu[i]) :
            sprintf(buf + index, "%02X", uu[i]);
        if (len < 0)
            return std::move(std::string(""));
        index += len;
    }
#endif // _MSC_VER
    return std::move(std::string(buf));
}

